#pragma once

#include "state.hpp"
#include <cstring>

namespace limbo::icmp::echo {

template <typename State, bool inc_sequence> struct Packet {
  using Context = typename State::Context;
  using Container = typename State::LowerPacket;
  using Customization = customization<details::base_state_t<State>>;
  using LowerState = typename State::LowerState;
  static constexpr uint8_t SZ = 4;

  static Result send(State &state, const Context &, Chunk buff,
                     const Chunk payload) {
    auto sz = payload.size() + SZ;
    if (buff.size() < sz) {
      auto code = (uint32_t)Errc::no_enough_space;
      return Result::make_error(code, &state);
    }

    auto start = (unsigned char *)buff.data();
    auto ptr = start;
    if constexpr (inc_sequence) {
      state.sequence_no++;
    }

    Customization::copy(ptr, &state.identifier, sizeof(state.identifier));
    ptr += 2;

    Customization::copy(ptr, &state.sequence_no, sizeof(state.sequence_no));
    ptr += 2;

    Customization::copy(ptr, payload.data(), payload.size());

    auto consumed = Chunk(start, sz);
    return Result::make_consumed(&state, consumed);
  }

  Result recv(Chunk buff, State &state, LowerState *icmp_state) {
    auto sz = buff.size();
    auto start = (unsigned char *)buff.data();
    auto ptr = start;
    if (sz < SZ) {
      return Result::make_demand(&state, SZ - sz);
    }
    container = &icmp_state->get_parsed();

    Customization::copy(&identifier, ptr, sizeof(identifier));
    ptr += 2;

    Customization::copy(&sequence_no, ptr, sizeof(identifier));
    ptr += 2;

    payload = Chunk(ptr, sz - SZ);

    auto consumed = Chunk(start, sz);
    return Result::make_consumed(&state, consumed);
  }

  Container *container;
  uint16_t identifier;
  uint16_t sequence_no = 0;
  Chunk payload;
};

} // namespace limbo::icmp::echo
